/****************************************************************
 * <strong>MFReflect反射类</strong>
 * <p>
 * 反射处理类
 * </p>
 *
 * @author AndyZhang 2011
 * @version 0.1
 * ToDo:
 ***************************************************************/

package com.thinkwide.data.ormapping;

import com.thinkwide.data.config.GlobalConfig;
import com.thinkwide.data.core.MFString;
import com.thinkwide.data.debug.MFDebug;
import com.thinkwide.data.ormapping.annotation.MFField;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


public class MFReflect {

    public static Field getPropertyField(Object instance, String name) {
        Field[] fs = instance.getClass().getDeclaredFields();
        for (Field f : fs) {
            if (f.getName().equalsIgnoreCase(name)) {
                return f;
            }
        }
        return null;
    }

    public static boolean setProperty(Object instance, String name, Object value) throws Throwable {
        Field field = getPropertyField(instance, name);
        field.setAccessible(true);
        field.set(instance, value);

        return true;

    }


    public static boolean copyObjectProperty(Object source, Object destination) throws Throwable {
        if (source == null) {
            return true;
        }
        Field[] fSource = source.getClass().getDeclaredFields();
        Field[] fDes = destination.getClass().getDeclaredFields();
        for (Field fs : fSource) {
            for (Field fd : fDes) {

                MFField att = fd.getAnnotation(MFField.class);
                if (att != null) {
                    if (fd.getName().equalsIgnoreCase(fs.getName())) {
                        fd.setAccessible(true);
                        fs.setAccessible(true);
                        fd.set(destination, fs.get(source));
                    }
                }
            }


        }
        return true;

    }

    public static boolean setPropertyList(Object instance, Map map) throws Throwable {
        List<String> list = getPropertyList(instance.getClass());
        for (String item : list) {
            Iterator keyValue = map.entrySet().iterator();
            while (keyValue.hasNext()) {
                Map.Entry entry = (Map.Entry) keyValue.next();
                String key = entry.getKey().toString();
                Object value = entry.getValue();
                if (MFString.compareNoCase(key, item)) {
                    if (isHaveProperty(instance, key)) {
                        setProperty(instance, key, value);
                    }
                }
            }
        }

        return true;

    }


    public static <T> List<T> getRequestObjectList(Class<T> classType, String prefix, HttpServletRequest request) throws Throwable {
        List<String> list = getPropertyList(classType);
        String newPrefix = "";

        if (MFString.notNullOrEmpty(prefix)) {
            newPrefix = prefix + ".";
        }

        List<T> Objlist = new ArrayList<T>();

        String[] values = request.getParameterValues(newPrefix + list.get(0));

        int i = 0;
        for (String vItem : values) {
            T instance = classType.newInstance();

            for (String item : list) {
                Object prop = MFReflect.getProperty(instance, item);
                String[] xValues = request.getParameterValues(newPrefix + item);

                if (prop != null) {

                    Class<?> type = MFReflect.getPropertyType(instance, item);
                    Object typedValue = null;
                    String value = xValues[i];
                    try {
                        typedValue = MFDbTypeTransfer.toJavaType(type, value);
                    } catch (Exception e) {
                        MFDebug.println("MFDbTypeTransfer.toJavaType %s can't change ", item);
                    }

                    MFReflect.setProperty(instance, item, typedValue);
                    i++;

                }
            }

            Objlist.add(instance);

        }
        return Objlist;
    }

    public static boolean setPropertyRequestExt(Object instance, HttpServletRequest request) throws Throwable {
        return setPropertyRequestExt(instance, "", request);
    }


    public static boolean setPropertyRequestExt(Object instance, String prefix, HttpServletRequest request) throws Throwable {
        List<String> list = getPropertyList(instance.getClass());
        String newPrefix = "";

        if (MFString.notNullOrEmpty(prefix)) {
            newPrefix = prefix + ".";
        }

        for (String item : list) {
            Object prop = MFReflect.getProperty(instance, item);

            if (prop != null) {
                if (prop.getClass() == ArrayList.class) {
                    String[] values = request.getParameterValues(newPrefix + item);
                    List<String> plist = Arrays.asList(values);
                    MFReflect.setProperty(instance, item, plist);
                    continue;
                } else {
                    Class<?> type = MFReflect.getPropertyType(instance, item);
                    Object typedValue = null;
                    String value = request.getParameter(newPrefix + item);
                    try {
                        typedValue = MFDbTypeTransfer.toJavaType(type, value);
                    } catch (Exception e) {
                        MFDebug.println("MFDbTypeTransfer.toJavaType %s can't change ", item);
                    }

                    MFReflect.setProperty(instance, item, typedValue);
                }

            }

        }

        return true;
    }

    public static boolean setPropertyRequest(Object instance, HttpServletRequest request) throws Throwable {

        Enumeration params = request.getParameterNames();
        while (params.hasMoreElements()) {
            String name = (String) params.nextElement();
            String value = request.getParameter(name);
            String values[] = null;
            boolean bValues = false;
            if (request.getParameterValues(name) != null) {
                values = request.getParameterValues(name);
                if (values.length > 1) {
                    bValues = true;
                }
            }

            if (MFString.notNullOrEmpty(name) && MFReflect.isHaveProperty(instance, name)) {
                Object prop = MFReflect.getProperty(instance, name);

                if (prop != null) {
                    if (prop.getClass() == ArrayList.class) {
                        List<String> list = Arrays.asList(values);
                        MFReflect.setProperty(instance, name, list);
                        continue;
                    }
                }

                if (bValues) {
                    List<String> list = Arrays.asList(values);
                    MFReflect.setProperty(instance, name, list);
                } else {
                    Class<?> type = MFReflect.getPropertyType(instance, name);
                    Object typedValue = null;
                    try {
                        typedValue = MFDbTypeTransfer.toJavaType(type, value);
                    } catch (Exception e) {
                        MFDebug.println("MFDbTypeTransfer.toJavaType %s can't change ", name);
                    }

                    MFReflect.setProperty(instance, name, typedValue);
                }

            }
        }
        return true;
    }

    public static Class<?> getPropertyType(Object instance, String name) throws Throwable {
        Field field = getPropertyField(instance, name);
        return field.getType();
    }

    public static boolean setDbProperty(Object instance, String name, Object value) throws Throwable {
        //Field field=instance.getClass().getDeclaredField(name);
        Field field = getPropertyField(instance, name);
        field.setAccessible(true);
        //field.set(instance, value);

        Object typedValue = MFDbTypeTransfer.toJavaType(
                field.getType(),
                value
        );
        field.set(instance, typedValue);

        /*if (value == null) {
            field.set(instance, value);
        } else {
            Object typedValue = MFDbTypeTransfer.toJavaType(
                    field.getType(),
                    value
            );
            field.set(instance, typedValue);
        }*/
        return true;

    }

    public static Object getProperty(Object instance, String name) throws Throwable {
        //Field field=instance.getClass().getDeclaredField(name);

        Field field = getPropertyField(instance, name);
        field.setAccessible(true);
        Object value = field.get(instance);
        return value;

    }

    public static boolean isHaveProperty(Object instance, String name) throws Throwable {
        Field[] fs = instance.getClass().getDeclaredFields();
        for (Field f : fs) {
            if (f.getName().equalsIgnoreCase(name)) {
                return true;
            }
        }
        return false;

    }

    public static String getRealPropertyName(Object instance, String name) throws Throwable {
        Field[] fs = instance.getClass().getDeclaredFields();
        if (GlobalConfig.AUTOCASE) {
            name = name.replace("_", "");
        }
        for (Field f : fs) {
            if (f.getName().equalsIgnoreCase(name)) {
                return f.getName();
            }
        }
        return null;

    }

    public static List<String> getPropertyList(Class<?> instance) {
        Field[] fs = instance.getDeclaredFields();
        List<String> ls = new ArrayList<String>();
        for (Field f : fs) {
            ls.add(f.getName());
        }
        return ls;

    }

    public static List<Field> getClassFields(Class classType, Class classAttr) {
        List<Field> list = new ArrayList<Field>();

        Field[] fields = classType.getDeclaredFields();
        for (Field field : fields) {
            if (classAttr != null) {
                if (field.isAnnotationPresent(classAttr)) {
                    list.add(field);
                }
            } else {
                list.add(field);
            }
        }

        Class superClass = classType.getSuperclass();
        if (superClass != null) {
            //list.addAll(getMapFields(superClass));
        }

        return list;
    }


}


